using System.Collections.Generic;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Jobs;
using UnityEngine.Profiling;

namespace JobDemo
{
    public class JobRunner : MonoBehaviour
    {
        [ContextMenu("OtherDemo")]
        public void OtherDemo()
        {
            Application.OpenURL("https://github.com/stella3d/job-system-cookbook.git");
        }

        /// <summary>
        /// 执行单独的Job.
        /// 最简化使用.
        /// </summary>
        [ContextMenu("1-SingleJob")]
        public void SingleJobRunner()
        {
            //result container
            var allResults = new NativeArray<float>(1, Allocator.TempJob);

            //Job construct
            var addJobIns = new AddJob()
            {
                a = 1f,
                b = 2f,
                result = allResults
            };

            //Handle
            JobHandle firstHandle = addJobIns.Schedule();
            firstHandle.Complete();

            //output
            Debug.Log(allResults[0]);

            //Dispose container
            allResults.Dispose();
        }

        /// <summary>
        /// Job依赖执行(执行顺序控制)
        /// </summary>
        [ContextMenu("2-JobDependence")]
        public void JobDependenceRunner()
        {
            //Auto dispose
            using (var allResults = new NativeArray<float>(1, Allocator.TempJob))
            {
                var addJobIns = new AddJob()
                {
                    a = 1f,
                    b = 2f,
                    result = allResults
                };

                JobHandle firstHandle = addJobIns.Schedule();

                var incJobIns = new IncreaseJob()
                {
                    result = allResults,
                };
                var secondHandle = incJobIns.Schedule(firstHandle);
                secondHandle.Complete();

                Debug.Log(allResults[0]);
            }
        }

        public int ParallelCounter = 2 ^ 16;

        [ContextMenu("2-ParallelJob")]
        public void ParallelJobRunner()
        {
            var a = new NativeArray<float>(ParallelCounter, Allocator.TempJob);
            var b = new NativeArray<float>(ParallelCounter, Allocator.TempJob);
            var allResults = new NativeArray<float>(ParallelCounter, Allocator.TempJob);

            for (int i = 0; i < ParallelCounter; i++)
            {
                a[i] = i;
                b[i] = i * 10;
            }

            var pJob = new ParallelAddJob()
            {
                a = a,
                b = b,
                result = allResults,
            };
            var pJobHandle = pJob.Schedule(ParallelCounter, SystemInfo.processorCount);
            pJobHandle.Complete();

            a.Dispose();
            b.Dispose();
            allResults.Dispose();
        }

        public void CommonJobRunner()
        {
            var a = new float[ParallelCounter];
            var b = new float[ParallelCounter];
            var result = new float[ParallelCounter];

            for (int i = 0; i < ParallelCounter; i++)
            {
                a[i] = i;
                b[i] = i * 10;
            }

            for (int i = 0; i < ParallelCounter; i++)
            {
                result[i] = a[i] + b[i];
            }
            a = null;
            b = null;
            result = null;
        }

        [ContextMenu("4-CompareParallel")]
        public void Compare()
        {
            Profiler.BeginSample("CommonJob");
            CommonJobRunner();
            Profiler.EndSample();

            Profiler.BeginSample("ParallelJob");
            ParallelJobRunner();
            Profiler.EndSample();
        }

        private void OnGUI()
        {
            if (GUI.Button(new Rect(100, 100, 150, 50), "CommonJob"))
            {
                Profiler.BeginSample("CommonJob");
                CommonJobRunner();
                Profiler.EndSample();
            }

            if (GUI.Button(new Rect(100, 400, 150, 50), "ParallelJob"))
            {
                Profiler.BeginSample("ParallelJob");
                ParallelJobRunner();
                Profiler.EndSample();
            }
        }

        #region Transfrom Job

        public int TransCount = 100;
        public float RotateSpeed = 1f;
        private TransformAccessArray transArray;
        private JobHandle transHandler;

        public void ParallelTransformJobRunner()
        {
            var prefab = GameObject.CreatePrimitive(PrimitiveType.Cube);
            prefab.GetComponent<Collider>().enabled = false;

            var list = new List<Transform>();
            while (TransCount > 0)
            {
                var instance = GameObject.Instantiate(prefab);
                instance.transform.position = Random.insideUnitSphere * 20f;
                list.Add(instance.transform);
                TransCount--;
            }

            transArray = new TransformAccessArray(list.ToArray());
            GameObject.Destroy(prefab);
        }

        private void Start()
        {
            ParallelTransformJobRunner();
        }

        private void Update()
        {
            var jobIns = new TransformJob()
            {
                Speed = RotateSpeed
            };

            transHandler = jobIns.Schedule(transArray);
        }

        private void LateUpdate()
        {
            transHandler.Complete();
        }

        private void OnDestroy()
        {
            transArray.Dispose();
        }

        #endregion Transfrom Job
    }
}